home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Surfer 2.0
/
Internet Surfer 2.0 (Wayzata Technology) (1996).iso
/
pc
/
text
/
mac
/
faqs.537
< prev
next >
Wrap
Text File
|
1996-02-12
|
28KB
|
754 lines
Frequently Asked Questions (FAQS);faqs.537
In order to get your login shell to execute the script (without
forking) you have to use the "." command (for the Bourne or Korn
shells) or the "source" command (for the C shell). I.e. you type
. myscript
to the Bourne or Korn shells, or
source myscript
to the C shell.
If all you are trying to do is change directory or set an
environment variable, it will probably be simpler to use a C
shell alias or Bourne/Korn shell function. See the "how do I get
the current directory into my prompt" section of this article for
some examples.
2.9) How do I redirect stdout and stderr separately in csh?
In csh, you can redirect stdout with ">", or stdout and stderr
together with ">&" but there is no direct way to redirect stderr
only. The best you can do is
( command >stdout_file ) >&stderr_file
which runs "command" in a subshell; stdout is redirected inside
the subshell to stdout_file, and both stdout and stderr from the
subshell are redirected to stderr_file, but by this point stdout
has already been redirected so only stderr actually winds up in
stderr_file.
If what you want is to avoid redirecting stdout at all, let sh
do it for you.
sh -c 'command 2>stderr_file'
2.10) How do I tell inside .cshrc if I'm a login shell?
From: msb@sq.com (Mark Brader)
Date: Mon, 26 Oct 1992 20:15:00 -0500
When people ask this, they usually mean either
How can I tell if it's an interactive shell? or
How can I tell if it's a top-level shell?
You could perhaps determine if your shell truly is a login shell
(i.e. is going to source ".login" after it is done with ".cshrc")
by fooling around with "ps" and "$$". Login shells generally
have names that begin with a '-'. If you're really interested in
the other two questions, here's one way you can organize your
.cshrc to find out.
if (! $?CSHLEVEL) then
#
# This is a "top-level" shell,
# perhaps a login shell, perhaps a shell started up by
# 'rsh machine some-command'
# This is where we should set PATH and anything else we
# want to apply to every one of our shells.
#
setenv CSHLEVEL 0
set home = ~username # just to be sure
source ~/.env # environment stuff we always want
else
#
# This shell is a child of one of our other shells so
# we don't need to set all the environment variables again.
#
set tmp = $CSHLEVEL
@ tmp++
setenv CSHLEVEL $tmp
endif
# Exit from .cshrc if not interactive, e.g. under rsh
if (! $?prompt) exit
# Here we could set the prompt or aliases that would be useful
# for interactive shells only.
source ~/.aliases
2.11) How do I construct a shell glob-pattern that matches all files
except "." and ".." ?
You'd think this would be easy.
* Matches all files that don't begin with a ".";
.* Matches all files that do begin with a ".", but
this includes the special entries "." and "..",
which often you don't want;
.[!.]* (Newer shells only; some shells use a "^" instead of
the "!"; POSIX shells must accept the "!", but may
accept a "^" as well; all portable applications shall
not use an unquoted "^" immediately following the "[")
Matches all files that begin with a "." and are
followed by a non-"."; unfortunately this will miss
"..foo";
.??* Matches files that begin with a "." and which are
at least 3 characters long. This neatly avoids
"." and "..", but also misses ".a" .
So to match all files except "." and ".." safely you have to use
3 patterns (if you don't have filenames like ".a" you can leave
out the first):
.[!.]* .??* *
Alternatively you could employ an external program or two and use
backquote substitution. This is pretty good:
`ls -a | sed -e '/^\.$/d' -e '/^\.\.$/d'`
(or `ls -A` in some Unix versions)
but even it will mess up on files with newlines, IFS characters
or wildcards in their names.
2.12) How do I find the last argument in a Bourne shell script?
Answer by:
Martin Weitzel <@mikros.systemware.de:martin@mwtech.uucp>
Maarten Litmaath <maart@nat.vu.nl>
If you are sure the number of arguments is at most 9, you can use:
eval last=\${$#}
In POSIX-compatible shells it works for ANY number of arguments.
The following works always too:
for last
do
:
done
This can be generalized as follows:
for i
do
third_last=$second_last
second_last=$last
last=$i
done
Now suppose you want to REMOVE the last argument from the list,
or REVERSE the argument list, or ACCESS the N-th argument
directly, whatever N may be. Here is a basis of how to do it,
using only built-in shell constructs, without creating subprocesses:
t0= u0= rest='1 2 3 4 5 6 7 8 9' argv=
for h in '' $rest
do
for t in "$t0" $rest
do
for u in $u0 $rest
do
case $# in
0)
break 3
esac
eval argv$h$t$u=\$1
argv="$argv \"\$argv$h$t$u\"" # (1)
shift
done
u0=0
done
t0=0
done
# now restore the arguments
eval set x "$argv" # (2)
shift
This example works for the first 999 arguments. Enough?
Take a good look at the lines marked (1) and (2) and convince
yourself that the original arguments are restored indeed, no
matter what funny characters they contain!
To find the N-th argument now you can use this:
eval argN=\$argv$N
To reverse the arguments the line marked (1) must be changed to:
argv="\"\$argv$h$t$u\" $argv"
How to remove the last argument is left as an exercise.
If you allow subprocesses as well, possibly executing nonbuilt-in
commands, the `argvN' variables can be set up more easily:
N=1
for i
do
eval argv$N=\$i
N=`expr $N + 1`
done
To reverse the arguments there is still a simpler method, that
even does not create subprocesses. This approach can also be
taken if you want to delete e.g. the last argument, but in that
case you cannot refer directly to the N-th argument any more,
because the `argvN' variables are set up in reverse order:
argv=
for i
do
eval argv$#=\$i
argv="\"\$argv$#\" $argv"
shift
done
eval set x "$argv"
shift
2.13) What's wrong with having '.' in your $PATH ?
A bit of background: the PATH environment variable is a list of
directories separated by colons. When you type a command name
without giving an explicit path (e.g. you type "ls", rather than
"/bin/ls") your shell searches each directory in the PATH list in
order, looking for an executable file by that name, and the shell
will run the first matching program it finds.
One of the directories in the PATH list can be the current
directory "." . It is also permissible to use an empty directory
name in the PATH list to indicate the current directory. Both of
these are equivalent
for csh users:
setenv PATH :/usr/ucb:/bin:/usr/bin
setenv PATH .:/usr/ucb:/bin:/usr/bin
for sh or ksh users
PATH=:/usr/ucb:/bin:/usr/bin export PATH
PATH=.:/usr/ucb:/bin:/usr/bin export PATH
Having "." somewhere in the PATH is convenient - you can type
"a.out" instead of "./a.out" to run programs in the current
directory. But there's a catch.
Consider what happens in the case where "." is the first entry
in the PATH. Suppose your current directory is a publically-
writable one, such as "/tmp". If there just happens to be a
program named "/tmp/ls" left there by some other user, and you
type "ls" (intending, of course, to run the normal "/bin/ls"
program), your shell will instead run "./ls", the other user's
program. Needless to say, the results of running an unknown
program like this might surprise you.
It's slightly better to have "." at the end of the PATH:
setenv PATH /usr/ucb:/bin:/usr/bin:.
Now if you're in /tmp and you type "ls", the shell will
search /usr/ucb, /bin and /usr/bin for a program named
"ls" before it gets around to looking in ".", and there
is less risk of inadvertently running some other user's
"ls" program. This isn't 100% secure though - if you're
a clumsy typist and some day type "sl -l" instead of "ls -l",
you run the risk of running "./sl", if there is one.
Some "clever" programmer could anticipate common typing
mistakes and leave programs by those names scattered
throughout public directories. Beware.
Many seasoned Unix users get by just fine without having
"." in the PATH at all:
setenv PATH /usr/ucb:/bin:/usr/bin
If you do this, you'll need to type "./program" instead
of "program" to run programs in the current directory, but
the increase in security is probably worth it.
--
Ted Timar - tmatimar@empress.com
Empress Software, 3100 Steeles Ave E, Markham, Ont., Canada L3R 8T3
Xref: bloom-picayune.mit.edu comp.unix.questions:51335 comp.unix.shell:8341 news.answers:4777
Path: bloom-picayune.mit.edu!senator-bedfellow.mit.edu!senator-bedfellow.mit.edu!usenet
From: tmatimar@empress.com (Ted M A Timar)
Newsgroups: comp.unix.questions,comp.unix.shell,news.answers
Subject: Unix - Frequently Asked Questions (3/7) [Frequent posting]
Supersedes: <unix-faq/faq/part3_723967331@athena.mit.edu>
Followup-To: comp.unix.questions
Date: 24 Dec 1992 06:03:13 GMT
Organization: Empress Software
Lines: 636
Approved: news-answers-request@MIT.Edu
Distribution: world
Expires: 21 Jan 1993 06:02:09 GMT
Message-ID: <unix-faq/faq/part3_725176929@athena.mit.edu>
References: <unix-faq/faq/contents_725176929@athena.mit.edu>
NNTP-Posting-Host: pit-manager.mit.edu
X-Last-Updated: 1992/12/09
Archive-name: unix-faq/faq/part3
Version: $Id: part3,v 2.1 92/12/04 07:43:49 tmatimar Exp $
These seven articles contain the answers to some Frequently Asked
Questions often seen in comp.unix.questions and comp.unix.shell.
Please don't ask these questions again, they've been answered plenty
of times already - and please don't flame someone just because they may
not have read this particular posting. Thank you.
These articles are divided approximately as follows:
1.*) General questions.
2.*) Relatively basic questions, likely to be asked by beginners.
3.*) Intermediate questions.
4.*) Advanced questions, likely to be asked by people who thought
they already knew all of the answers.
5.*) Questions pertaining to the various shells, and the differences.
6.*) An overview of Unix variants.
7.*) An comparison of configuration management systems (RCS, SCCS).
This article includes answers to:
3.1) How do I find out the creation time of a file?
3.2) How do I use "rsh" without having the rsh hang around
until the remote command has completed?
3.3) How do I truncate a file?
3.4) Why doesn't find's "{}" symbol do what I want?
3.5) How do I set the permissions on a symbolic link?
3.6) How do I "undelete" a file?
3.7) How can a process detect if it's running in the background?
3.8) Why doesn't redirecting a loop work as intended? (Bourne shell)
3.9) How do I run 'passwd', 'ftp', 'telnet', 'tip' and other interactive
programs from a shell script or in the background?
3.10) How do I find out the process ID of a program with a particular
name from inside a shell script or C program?
3.11) How do I check the exit status of a remote command
executed via "rsh" ?
3.12) Is it possible to pass shell variable settings into an awk program?
3.13) How do I get rid of zombie processes that persevere?
3.14) How do I get lines from a pipe as they are written instead of
only in larger blocks.
If you're looking for the answer to, say, question 3.5, and want to skip
everything else, you can search ahead for the regular expression "^3.5)".
While these are all legitimate questions, they seem to crop up in
comp.unix.questions or comp.unix.shell on an annual basis, usually
followed by plenty of replies (only some of which are correct) and then
a period of griping about how the same questions keep coming up. You
may also like to read the monthly article "Answers to Frequently Asked
Questions" in the newsgroup "news.announce.newusers", which will tell
you what "UNIX" stands for.
With the variety of Unix systems in the world, it's hard to guarantee
that these answers will work everywhere. Read your local manual pages
before trying anything suggested here. If you have suggestions or
corrections for any of these answers, please send them to to
tmatimar@empress.com.
3.1) How do I find out the creation time of a file?
You can't - it isn't stored anywhere. Files have a last-modified
time (shown by "ls -l"), a last-accessed time (shown by "ls -lu")
and an inode change time (shown by "ls -lc"). The latter is often
referred to as the "creation time" - even in some man pages -
but that's wrong; it's also set by such operations as mv, ln,
chmod, chown and chgrp.
The man page for "stat(2)" discusses this.
3.2) How do I use "rsh" without having the rsh hang around until the
remote command has completed?
(See note in question 2.7 about what "rsh" we're talking about.)
The obvious answers fail:
rsh machine command &
or rsh machine 'command &'
For instance, try doing rsh machine 'sleep 60 &' and you'll see
that the 'rsh' won't exit right away. It will wait 60 seconds
until the remote 'sleep' command finishes, even though that
command was started in the background on the remote machine. So
how do you get the 'rsh' to exit immediately after the 'sleep' is
started?
The solution - if you use csh on the remote machine:
rsh machine -n 'command >&/dev/null </dev/null &'
If you use sh on the remote machine:
rsh machine -n 'command >/dev/null 2>&1 </dev/null &'
Why? "-n" attaches rsh's stdin to /dev/null so you could run the
complete rsh command in the background on the LOCAL machine.
Thus "-n" is equivalent to another specific "< /dev/null".
Furthermore, the input/output redirections on the REMOTE machine
(inside the single quotes) ensure that rsh thinks the session can
be terminated (there's no data flow any more.)
Note: The file that you redirect to/from on the remote machine
doesn't have to be /dev/null; any ordinary file will do.
In many cases, various parts of these complicated commands
aren't necessary.
3.3) How do I truncate a file?
The BSD function ftruncate() sets the length of a file. Xenix -
and therefore SysV r3.2 and later - has the chsize() system
call. For other systems, the only kind of truncation you can do
is truncation to length zero with creat() or open(..., O_TRUNC).
3.4) Why doesn't find's "{}" symbol do what I want?
"find" has a -exec option that will execute a particular command
on all the selected files. Find will replace any "{}" it sees
with the name of the file currently under consideration.
So, some day you might try to use "find" to run a command on
every file, one directory at a time. You might try this:
find /path -type d -exec command {}/\* \;
hoping that find will execute, in turn
command directory1/*
command directory2/*
...
Unfortunately, find only expands the "{}" token when it appears
by itself. Find will leave anything else like "{}/*" alone, so
instead of doing what you want, it will do
command {}/*
command {}/*
...
once for each directory. This might be a bug, it might be a
feature, but we're stuck with the current behaviour.
So how do you get around this? One way would be to write a
trivial little shell script, let's say "./doit", that consists of
command "$1"/*
You could then use
find /path -type d -exec ./doit {} \;
Or if you want to avoid the "./doit" shell script, you can use
find /path -type d -exec sh -c 'command $0/*' {} \;
(This works because within the 'command' of "sh -c 'command' A B C ...",
$0 expands to A, $1 to B, and so on.)
or you can use the construct-a-command-with-sed trick
find /path -type d -print | sed 's:.*:command &/*:' | sh
If all you're trying to do is cut down on the number of times
that "command" is executed, you should see if your system has the
"xargs" command. Xargs reads arguments one line at a time from
the standard input and assembles as many of them as will fit into
one command line. You could use
find /path -print | xargs command
which would result in one or more executions of
command file1 file2 file3 file4 dir1/file1 dir1/file2
Unfortunately this is not a perfectly robust or secure solution.
Xargs expects its input lines to be terminated with newlines, so
it will be confused by files with odd characters such as newlines
in their names.
3.5) How do I set the permissions on a symbolic link?
Permissions on a symbolic link don't really mean anything. The
only permissions that count are the permissions on the file that
the link points to.
3.6) How do I "undelete" a file?
Someday, you are going to accidentally type something like
"rm * .foo", and find you just deleted "*" instead of "*.foo".
Consider it a rite of passage.
Of course, any decent systems administrator should be doing
regular backups. Check with your sysadmin to see if a recent
backup copy of your file is available. But if it isn't, read
on.
For all intents and purposes, when you delete a file with "rm" it
is gone. Once you "rm" a file, the system totally forgets which
blocks scattered around the disk comprised your file. Even
worse, the blocks from the file you just deleted are going to be
the first ones taken and scribbled upon when the system needs
more disk space. However, never say never. It is theoretically
possible *if* you shut down the system immediately after the "rm"
to recover portions of the data. However, you had better have a
very wizardly type person at hand with hours or days to spare to
get it all back.
Your first reaction when you "rm" a file by mistake is why not
make a shell alias or procedure which changes "rm" to move files
into a trash bin rather than delete them? That way you can
recover them if you make a mistake, and periodically clean out
your trash bin. Two points: first, this is generally accepted
as a *bad* idea. You will become dependent upon this behaviour
of "rm", and you will find yourself someday on a normal system
where "rm" is really "rm", and you will get yourself in trouble.
Second, you will eventually find that the hassle of dealing with
the disk space and time involved in maintaining the trash bin, it
might be easier just to be a bit more careful with "rm". For
starters, you should look up the "-i" option to "rm" in your
manual.
If you are still undaunted, then here is a possible simple
answer. You can create yourself a "can" command which moves
files into a trashcan directory. In csh(1) you can place the
following commands in the ".login" file in your home directory:
alias can 'mv \!* ~/.trashcan' # junk file(s) to trashcan
alias mtcan 'rm -f ~/.trashcan/*' # irretrievably empty trash
if ( ! -d ~/.trashcan ) mkdir ~/.trashcan # ensure trashcan exists
You might also want to put a:
rm -f ~/.trashcan/*
in the ".logout" file in your home directory to automatically
empty the trash when you log out. (sh and ksh versions are left
as an exercise for the reader.)
MIT's Project Athena has produced a comprehensive
delete/undelete/expunge/purge package, which can serve as a
complete replacement for rm which allows file recovery. This
package was posted to comp.sources.misc (volume 17, issue
023-026)
3.7) How can a process detect if it's running in the background?
First of all: do you want to know if you're running in the
background, or if you're running interactively? If you're
deciding whether or not you should print prompts and the like,
that's probably a better criterion. Check if standard input
is a terminal:
sh: if [ -t 0 ]; then ... fi
C: if(isatty(0)) { ... }
In general, you can't tell if you're running in the background.
The fundamental problem is that different shells and different
versions of UNIX have different notions of what "foreground" and
"background" mean - and on the most common type of system with a
better-defined notion of what they mean, programs can be moved
arbitrarily between foreground and background!
UNIX systems without job control typically put a process into the
background by ignoring SIGINT and SIGQUIT and redirecting the
standard input to "/dev/null"; this is done by the shell.
Shells that support job control, on UNIX systems that support job
control, put a process into the background by giving it a process
group ID different from the process group to which the terminal
belongs. They move it back into the foreground by setting the
terminal's process group ID to that of the process. Shells that
do *not* support job control, on UNIX systems that support job
control, typically do what shells do on systems that don't
support job control.
3.8) Why doesn't redirecting a loop work as intended? (Bourne shell)
Take the following example:
foo=bar
while read line
do
# do something with $line
foo=bletch
done < /etc/passwd
echo "foo is now: $foo"
Despite the assignment ``foo=bletch'' this will print
``foo is now: bar'' in many implementations of the Bourne shell.
Why? Because of the following, often undocumented, feature of
historic Bourne shells: redirecting a control structure (such as
a loop, or an ``if'' statement) causes a subshell to be created,
in which the structure is executed; variables set in that
subshell (like the ``foo=bletch'' assignment) don't affect the
current shell, of course.
The POSIX 1003.2 Shell and Tools Interface standardization
committee forbids the behaviour described above, i.e. in P1003.2
conformant Bourne shells the example will print ``foo is now:
bletch''.
In historic (and P1003.2 conformant) implementations you can use
the following `trick' to get around the redirection problem:
foo=bar
# make file descriptor 9 a duplicate of file descriptor 0 (stdin);
# then connect stdin to /etc/passwd; the original stdin is now
# `remembered' in file descriptor 9; see dup(2) and sh(1)
exec 9<&0 < /etc/passwd
while read line
do
# do something with $line
foo=bletch
done
# make stdin a duplicate of file descriptor 9, i.e. reconnect
# it to the original stdin; then close file descriptor 9
exec 0<&9 9<&-
echo "foo is now: $foo"
This should always print ``foo is now: bletch''.
Right, take the next example:
foo=bar
echo bletch | read foo
echo "foo is now: $foo"
This will print ``foo is now: bar'' in many implementations,
``foo is now: bletch'' in some others. Why? Generally each part
of a pipeline is run in a different subshell; in some
implementations though, the last command in the pipeline is made
an exception: if it is a builtin command like ``read'', the
current shell will execute it, else another subshell is created.
POSIX 1003.2 allows both behaviours so portable scripts cannot
depend on any of them.
3.9) How do I run 'passwd', 'ftp', 'telnet', 'tip' and other interactive
programs from a shell script or in the background?
These programs expect a terminal interface. Shells makes no
special provisions to provide one. Hence, such programs cannot
be automated in shell scripts.
The 'expect' program provides a programmable terminal interface
for automating interaction with such programs. The following
expect script is an example of a non-interactive version of
passwd(1).
# username is passed as 1st arg, password as 2nd
set password [index $argv 2]
spawn passwd [index $argv 1]
expect "*password:"
send "$password\r"
expect "*password:"
send "$password\r"
expect eof
expect can partially automate interaction which is especially
useful for telnet, rlogin, debuggers or other programs that have
no built-in command language. The distribution provides an
example script to rerun rogue until a good starting configuration
appears. Then, control is given back to the user to enjoy the game.
Fortunately some programs have been written to manage the
connection to a pseudo-tty so that you can run these sorts of
programs in a script.
To get expect, email "send pub/expect/expect.shar.Z" to
library@cme.nist.gov or anonymous ftp same from
durer.cme.nist.gov.
Another solution is provided by the pty 4.0 program, which runs a
program under a pseudo-tty session and was posted to
comp.sources.unix, volume 25. A pty-based solution using named
pipes to do the same as the above might look like this:
#!/bin/sh
/etc/mknod out.$$ p; exec 2>&1
( exec 4<out.$$; rm -f out.$$
<&4 waitfor 'password:'
echo "$2"
<&4 waitfor 'password:'
echo "$2"
<&4 cat >/dev/null
) | ( pty passwd "$1" >out.$$ )
Here, 'waitfor' is a simple C program that searches for
its argument in the input, character by character.
A simpler pty solution (which has the drawback of not
synchronizing properly with the passwd program) is
#!/bin/sh
( sleep 5; echo "$2"; sleep 5; echo "$2") | pty passwd "$1"
3.10) How do I find out the process ID of a program with a particular
name from inside a shell script or C program?
In a shell script:
There is no utility specifically designed to map between program
names and process IDs. Furthermore, such mappings are often
unreliable, since it's possible for more than one process to have
the same name, and since it's possible for a process to change
its name once it starts running. However, a pipeline like this
can often be used to get a list of processes (owned by you) with
a particular name:
ps ux | awk '/name/ && !/awk/ {print $2}'
You replace "name" with the name of the process for which you are
searching.
The general idea is to parse the output of ps, using awk or grep
or other utilities, to search for the lines with the specified
name on them, and print the PID's for those lines. Note that the
"!/awk/" above prevents the awk process for being listed.
You may have to change the arguments to ps, depending on what
kind of Unix you are using.
In a C program:
Just as there is no utility specifically designed to map between
program names and process IDs, there are no (portable) C library
functions to do it either.
However, some vendors provide functions for reading Kernel
memory; for example, Sun provides the "kvm_" functions, and Data
General provides the "dg_" functions. It may be possible for any
user to use these, or they may only be useable by the super-user
(or a user in group "kmem") if read-access to kernel memory on
your system is restricted. Furthermore, these functions are
often not documented or documented badly, and might change from
release to release.